<!DOCTYPE html>
<html lang="cmn-Hans-CN">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width,initial-scale=1.0">
	<title>Vue组件进阶</title>
	<link rel="shortcut icon" href="../../source/favicon.ico">
	<link rel="bookmark" href="../../source/favicon.ico">
	<link rel="stylesheet" href="../plugin/bootstrap.min.css">
	<link rel="stylesheet" href="../css/common.css">
	<style>
		#app-9 span{
			display: inline-block;
			vertical-align: middle;
		}
		.resStyle {
			padding: 0 12px;
			background-color: #eb981e;
			border: 1px solid #9d6416;
			border-radius: 5px;
			text-align: center;
			font-size: 32px;
			line-height: 45px;
			color: #ffffff;
		}
	</style>
</head>
<body>
	<header>
		<nav>
			<ul class="breadcrumb">
				<li><a href="../index.html">首页</a></li>
				<li class="active">Vue组件进阶</li>
			</ul>
		</nav>
	</header>

	<main>
		<article>
            <h1>Vue组件进阶</h1>
			<section id="app-1">
				<h2 class="section_h2">#app-1：通过该例子来再次认识组件</h2>
				<div>
					<input
						class="form-control"
						v-model="newPoetryText"
						@keydown.enter="addNewPoetry"
						placeholder="添加内容后按下Enter键"
					>
					<ul class="mt-10">
						<li
							is="poetry-item"
							v-for="(poetry, index) in poetryList"
							:prop="poetry"
							@remove="poetryList.splice(index, 1)"
							:key="poetry.id"
						></li>
					</ul>
				</div>

				<tips class="comment">在该段代码中，li标签内部是通过一个叫做is的属性指定组件名的，这是因为在HTML代码中某些标签是具有一定的固定结构的，如果固定结构不完整可能在有些环境中报出不符合W3C标准的异常，如ol-li、ul-li、select-option、dl-dt/dd、tr-td（thead-tr-th/tbody-tr-td）这样的结构。如果要想这些标签的子元素“欺骗”浏览器，让这些子元素以组件的方式渲染，这个时候就可以用is这个属性来指定组件的名称，具体见“<a href="https://cn.vuejs.org/v2/guide/components.html#%E8%A7%A3%E6%9E%90-DOM-%E6%A8%A1%E6%9D%BF%E6%97%B6%E7%9A%84%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9" target="_blank" rel="noopener noreferrer">官方文档——解析DOM模板时的注意事项</a>”。</tips>
			</section>

			<section id="app-2">
				<h2 class="section_h2">#app-2：再从基础认识组件</h2>
				<base-component></base-component>
				<!-- 尝试注释掉上面的组件，并解开下面组件的注释 -->
				<!-- <base-component :content='dataTxt'></base-component>
				<base-component content='这是一个显示文本的基本组件 -- by:child-component'></base-component> -->
				
				<tips class="comment">该示例展现了一个组件的三种最基本形态。找到对应HTML和JS代码部分，按注释说明进行操作，然后进入浏览器刷新查看效果，从而思考对Vue组件的基本认识</tips>
			</section>
			
			<section id="app-3">
				<h2 class="section_h2">#app-3：通过一个外部对象创建局部组件</h2>
				<local-component-first 
					style="color:#d3974a"
					title="外部模版定义-1"
					content="这是一个外部对象，为私有组件提供相册展示的模版"
				></local-component-first>
				<local-component-second 
					style="color:#e0658e"
					title="外部模版定义-2"
					content="这是一个外部对象，为私有组件提供显示现在时间的模版"
                ></local-component-second>
				
				<tips class="comment">该示例展示了在同一个Vue实例里引用多个组件的用法。简略地体现了组件化编程的思想--一个页面的内容是有一个或者多个可配置的组件“拼装”而成的，而组件间相互独立，结构清晰，分工明确，可以实现各自的功能。不会产生传统页面变量和作用域难以管理的问题。</tips>
			</section>

			<section id="app-4">
				<h2 class="section_h2">#app-4：组件在一些限制了HTML结构内的表示方法（带属性传值）</h2>
				<table class="table table-bordered">
					<thead class="bg-theme">
						<tr>
							<th v-for="item in thData">{{item}}</th>
						</tr>
					</thead>
					<tbody>
						<tr is="table-row" :prop="naruto" :key="naruto.id" v-for="naruto in tdData"></tr>
					</tbody>
				</table>

				<tips class="comment">该示例相对简单，只是体现了标签v-for指令和组件v-for指令的异同。注意表头是通过标签指令渲染而成，而表体是通过组件的指令渲染而成的，而表体中的&lt;tr&gt;是必须的HTML结构，所以标签名不能直接为组件名，否则可能造成渲染错误，需要用“is”属性来指定组件的名称（原理同本章的第一节的内容）。</tips>
			</section>

			<section id="app-5">
				<h2 class="section_h2">{{smallHead}}：组件复用的差异性</h2>
				<!-- 组件复用5次，只是使用不同的class样式 -->
				<data-component :class="buttonStyle.primary"></data-component>
				<data-component :class="buttonStyle.success"></data-component>
				<data-component :class="buttonStyle.info"></data-component>
				<data-component :class="buttonStyle.warning"></data-component>
				<data-component :class="buttonStyle.danger"></data-component>

				<tips class="comment">进入对应本章的JS源码的“示例-5”部分，按照注释要求修改代码，然后再回到视图进行点击操作，观察区别，并思考造成这一差异的原因。
					<p>造成使用两种不同定义位置的数据源在视图上事件的原因，就是组件内有独立的作用域，每次调用的组件可以认为都是一个新的实例，每个同名组件之间的数据实际上是独立的。而当所有同名组件都去使用一个外部数据源，那这些组件的数据源都会指向内存的同一个地址，从而修改一个组件内的数据会影响到其它所有同名的组件。</p>
				</tips>
			</section>

			<section id="app-6">
				<h2 class="section_h2">{{smallHead}}：父子组件的通信</h2>
				<child-component communicate="通过自定义组件属性来完成父子组件的解耦"></child-component>
				<!-- 这里需要注意组件属性的两种命名的转换，即组件属性中的camelCased（驼峰式）命名在HTML中要转换为kebab-case（连线式）命名，否则将会出现Vue的警告，功能也会失效 -->
				<child-component communicate="这样组件就可以独立了" style-prop="color:#239feb; font: 36px '华文隶书','隶书','宋体'"></child-component>

				<tips class="comment">本节主要体现的是如何通过自定义属性（不带v-bind的情况）去实现子组件的解耦，并且在父组件环境上省略一些子组件组件上已经定义好的属性特征。如该示例中的第一个父组件环境的组件就省略了在父组件上提供的“styleProp”属性，组件编译过程仍然正常执行。
                    <p>该示例中要注意对应的JS源码中的props属性内的“styleProp”及视图中“style-prop”的对应。因为HTML是不区分大小写的，所以在HTML视图中无论是标签名还是属性名都应该一律使用小写字母，当JS为全大写字母或者使用驼峰命名时，在HTML中都应该使用小写，如果遇到驼峰命名的变量，在HTML应该转换为连线式的命名方式，在Vue的内部会自动进行转换对应</p>
                </tips>
			</section>

			<section id="app-7">
				<h2 class="section_h2">#app-7：子组件向父组件或根实例传值的方式</h2>
				<child-component @show-text="getChildData"></child-component>
				<div>
					子组件传过来的值是：
					<span :style="getStyle">{{childData}}</span>
				</div>

				<tips class="comment">子组件要向“上层”传值，只能通过事件触发，而事件内部关键的方法是“$emit”。“$emit”方法的第一个参数是组件上用“@show-text”表示的自定义事件名称，而接收这个事件的值的方法为“getChildData”（也就是说传递顺序是：@show-text =&gt; getChildData），然后再在父组件或根实例的Methods对象中去执行这个方法，而方法内接收的参数的第一个对应的是子组件内$emit方法的第二个参数，而第二位参数对应的是子组件内$emit方法的第三个参数，以此类推。</tips>
			</section>

			<section id="app-8">
				<h2 class="section_h2">#app-8：在不使用methods对象内的方法的情况下向父组件传值</h2>
				<child-post-value @postvalue="numData *= $event"></child-post-value>
				<div>结果为：{{numData}}</div>

				<tips class="comment">在根实例或父组件环境下，可以通过“$event”来获取子组件通过事件内的“$emit”抛出来的值，详见“<a href="https://cn.vuejs.org/v2/guide/components.html#%E4%BD%BF%E7%94%A8%E4%BA%8B%E4%BB%B6%E6%8A%9B%E5%87%BA%E4%B8%80%E4%B8%AA%E5%80%BC" target="_blank" rel="noopener noreferrer">官方文档——使用事件抛出一个值</a>”。这种方式虽然更为简便，但是需要注意的是使用这种方式传值一次性只能接受一个参数值。</tips>
			</section>

			<section id="app-9">
				<h2 class="section_h2">#app-9：当Vue的组件在团队中使用时，属性的数据类型验证提示</h2>
				<!-- 尝试修改组件属性的值，可选值有：str（字符串）、num（数字）和bool（布尔值）,然后观察浏览器控制台 -->
				<check-component></check-component>

				<tips class="comment">进入对应该示例对应的HTML部分，将“:prop-string”属性的值按照注释部分提供的几个值进行修改，并在每次修改之后观察控制台的输出结果（值为“str”时不会有输出），然后通过JS文件对应部分理解控制台输出结果的原因。注意，如果页面有配置“Vue.config.silent”属性，请确保该属性的值为“false”，否则JS部分的验证将无效。
                    <p>记住，在大型的团队项目中，总是建议将组件内的“props”定义为一个对象形式，并指定数据类型，更大程度地避免组件传值类型的不确定性。</p>
                    <p>对props限制的类型有（首字母必须大写）：</p>
                    <ul>
                        <li>String</li>
                        <li>Number</li>
                        <li>Boolean</li>
                        <li>Function</li>
                        <li>Object</li>
                        <li>Array</li>
                        <li>Symbol</li>
                    </ul>
                </tips>
			</section>

		</article>
	</main>

	<script src="../plugin/vue.js"></script>
	<script src="../js/component-levup.js"></script>
</body>
</html>